package com.geor.gcv.core.mat;

import java.util.ArrayList;
import java.util.List;

import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import org.slf4j.Logger;

import com.geor.common.log.Loggers;
import com.geor.gcv.core.model.Area;
import com.geor.gcv.core.process.GrayProcess;
import com.geor.gcv.core.process.ResizeProcess;
import com.geor.gcv.core.test.Tester;

/**
 * 纠偏处理的霍夫变换实现
 * 
 * @author geor.yuli
 *
 */
public class HoughTiltHander implements TiltHander {
	Tester tester = new Tester();
	static Logger log = Loggers.get();

	public HoughTiltHander() {
	}

	Mat imgLine = null;

	public Mat getImgLine() {
		return imgLine;
	}

	@Override
	public TiltWapper excute(Mat src) {
		try {
			double scale = 0.5d;
			Mat img = new ResizeProcess(new GrayProcess(src), scale).process();
			// 高斯滤波去除噪点干扰
			Mat mat = new Mat();
			Imgproc.GaussianBlur(img, mat, new Size(5, 5), 0);
			Mats.releseMat(img);

			// 腐蚀一次去除条码干扰
			Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(10, 10));
			Imgproc.morphologyEx(mat, mat, Imgproc.MORPH_ERODE, element);
			Mats.releseMat(element);

			Mat edge = new Mat();
			Imgproc.Canny(mat, edge, 50, 150);
			Mats.releseMat(mat);

			tester.showNewWindow("纠偏过程图像", edge);

			// 找出最大轮廓
			List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
			Mat hierarchy = new Mat();
			Imgproc.findContours(edge, contours, hierarchy, Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_NONE);
			Mats.releseMat(hierarchy);

			int maxArea = 0;
			Rect maxRect = null;
			for (int i = 0; i < contours.size(); i++) {
				Rect rect = Imgproc.boundingRect(contours.get(i));
				if (rect.width * rect.height > maxArea) {
					maxArea = rect.width * rect.height;
					maxRect = rect;
				}
				Mats.releseMat(contours.get(i));
			}

			if (null != maxRect) {
				edge = Mats.screenShoot(edge, new Area(maxRect.x, maxRect.y, maxRect.width, maxRect.height));
			}

			Mat lines = new Mat();
			Imgproc.HoughLinesP(edge, lines, 1, Math.PI / 180, 100, 80, 5);

			int lineNum = lines.cols();

			if (lineNum < 30) {
				Imgproc.HoughLinesP(edge, lines, 1, Math.PI / 180, 100, 30 + lineNum, 5);
			}
			Mats.releseMat(edge);

			List<Float> degrees = new ArrayList<Float>();
			Point[][] links = new Point[lines.cols()][2];
			// 绘制线条
			for (int i = 0; i < lines.cols(); i++) {
				Point pta = new Point(lines.get(0, i)[0], lines.get(0, i)[1]);
				Point ptb = new Point(lines.get(0, i)[2], lines.get(0, i)[3]);

				links[i][0] = pta;
				links[i][1] = ptb;

				double width = ptb.x - pta.x;
				double height = ptb.y - pta.y;
				// log.trace(String.format("width: %f, height: %f", width,
				// height));
				float degree = (float) (180 / Math.PI * Math.atan((float) Math.abs(height) / Math.abs(width)));
				if (height < 0) {
					degree = 180 - degree;
				}
				degrees.add(degree);
			}

			Mats.releseMat(lines);

			// 角度分布分析
			// 角度分区
			int offset = 3;
			int slot = 360 / offset;
			int[] counter = new int[slot];
			for (Float degree : degrees) {
				// degree = Math.min(degree, 180 - degree);
				log.debug("degree:" + degree.floatValue());
				int dindex = (int) (degree / offset);
				counter[dindex]++;
			}

			// 最大数量
			int slotNum = 0;
			// 最大数量位置
			int slotIndex = 0;
			for (int i = 0; i < counter.length; i++) {
				if (counter[i] > slotNum) {
					slotNum = counter[i];
					slotIndex = i;
				}
			}
			log.debug(String.format("degree area: %d, [%d - %d]", slotIndex, slotIndex * offset,
					(slotIndex + 1) * offset));

			// 精确分布
			int[] counter2 = new int[offset];
			for (Float degree : degrees) {
				if (degree > slotIndex * offset && degree < (slotIndex + 1) * offset) {
					int index = 0;
					if (slotIndex == 0) {
						index = (int) degree.floatValue();
					} else {
						index = (int) degree.floatValue() % slotIndex;
					}
					counter2[index]++;
				}
			}
			// 最大数量
			int accNum = 0;
			// 最大数量位置
			int accIndex = 0;
			for (int i = 0; i < counter2.length; i++) {
				if (counter2[i] > accNum) {
					accNum = counter2[i];
					accIndex = i;
				}
			}

			int degree = -1 * (slotIndex * offset + accIndex);
			log.debug(String.format("get degree: %d", degree));

			return new TiltWapper(degree, false);
		} finally {
			Mats.releseMat(src);
		}
	}

}
